# -*- coding:utf-8 -*-
from flask import Flask, request, Response, json
import IPy
import log
import logging
import pexpect
import conf
import time
import os

app = Flask(__name__)

log.init_log("./log/eip")

PROMPT = b'>'
PROMT = b']'
cmd = {
    b'sys': b'sys',
    b'inbehavior': b'traffic behavior %(limit)sM',
    b'incar': b'car cir %(limit)s cbs %(rate)s ebs 0 green pass red discard yellow pass',
    b'outbehavior': b'traffic behavior %(limit)sM',
    b'outcar': b'car cir %(limit)s cbs %(rate)s ebs 0 green pass red discard yellow pass',
    b'acl': b'acl advanced name acl.%(eip)s',
    b'rule10': b'rule 10 permit ip source %(eip)s 0',
    b'rule20': b'rule 20 permit ip destination %(eip)s 0',
    b'traffic': b'traffic classifier %(eip)s operator and',
    b'ifmatch': b'if-match acl name acl.%(eip)s',
    b'inpolicy': b'qos policy intranet',
    b'inlimit': b'classifier %(eip)s behavior %(limit)sM',
    b'outpolicy': b'qos policy outnetlimit',
    b'outlimit': b'classifier %(eip)s behavior %(limit)sM'
}

deletecmd = {
    b'sys': b'sys',
    b'undoacl': b'undo acl advanced name acl.%(eip)s',
    b'inpolicy': b'qos policy intranet',
    b'inlimit': b'undo classifier %(eip)s',
    b'outpolicy': b'qos policy outnetlimit',
    b'outlimit': b'undo classifier %(eip)s',
    b'exit': b'exit',
    b'undotraffic': b'undo traffic classifier %(eip)s'
}

routersinfo = []


def init_route():
    routerservers = os.environ['RoutersAddr'].split(";;;")
    # routerservers = '172.252.0.1###admin###Yunshu@123;;;172.252.0.1###admin###Yunshu@123'.split(";;;")
    # routerservers = '10.0.46.150###root###networkTeam###root###networkTeam'.split(";")
    logging.info('###########raw routeinfo:%s' % routerservers)
    for valueinfo in routerservers:
        value = valueinfo.split("###")
        logging.info('######route:%s' % value)
        item = {}
        item['ip'] = value[0]
        item['username'] = value[1]
        item['passwd'] = value[2]
        routersinfo.append(item)
    logging.info('routesinfo:%s' % routersinfo)


@app.route('/eip_rateLimit', methods=['PUT'])  # url,1. decorator=app.route() 2. decorator(first_flask)中国
def eip_rateLimit():
    try:
        reqbody = request.get_json()
        logging.info("reqbody %s" % reqbody)
    except Exception as e:
        logging.exception(e)
        res = Response(json.dumps({'rc': 1000, 'msg': str(e)}), content_type='application/json')
        return res, 200
    floating_ip_address = reqbody['floating_ip_address']
    d = is_ip(floating_ip_address)
    if d != "yes":
        res = Response(json.dumps({'rc': 1001, 'msg': d}), content_type='application/json')
        return res, 200
    tx_averateLimit = reqbody['tx_averateLimit']
    d = is_ratelimit(tx_averateLimit)
    if d != "yes":
        res = Response(json.dumps({'rc': 1002, 'msg': d}), content_type='application/json')
        return res, 200
    rx_averateLimit = reqbody['rx_averateLimit']
    d = is_ratelimit(rx_averateLimit)
    if d != "yes":
        res = Response(json.dumps({'rc': 1003, 'msg': d}), content_type='application/json')
        return res, 200

    for value in routersinfo:
        try:
            logging.info('######value%s' % value)
            tn = login(value['ip'], value['username'], value['passwd'])
        except Exception as e:
            logging.exception(e)
            res = Response(json.dumps({'rc': 4, 'msg': str(e)}), content_type='application/json')
            return res, 200
        logging.info("login sucess!")
        try:
            eip_limit(tn, floating_ip_address, tx_averateLimit, rx_averateLimit)
        except Exception as e:
            tn.close()
            logging.exception(e)
            res = Response(json.dumps({'rc': 2002, 'msg': str(e)}), content_type='application/json')
            return res, 200
    return Response(json.dumps({'rc': 0, 'msg': 'sucess'}), content_type='application/json'), 200


@app.route('/eip_delete', methods=['PUT'])  # url,1. decorator=app.route() 2. decorator(first_flask)中国
def eip_withdraw():
    try:
        reqbody = request.get_json()
        logging.info("reqbody %s" % reqbody)
    except Exception as e:
        logging.exception(e)
        res = Response(json.dumps({'rc': 1000, 'msg': str(e)}), content_type='application/json')
        return res, 200
    floating_ip_address = reqbody['floating_ip_address']
    d = is_ip(floating_ip_address)
    if d != "yes":
        res = Response(json.dumps({'rc': 1001, 'msg': d}), content_type='application/json')
        return res, 200

    for value in routersinfo:
        try:
            logging.info('######value%s' % value)
            tn = login(value['ip'], value['username'], value['passwd'])
        except Exception as e:
            logging.exception(e)
            res = Response(json.dumps({'rc': 4, 'msg': str(e)}), content_type='application/json')
            return res, 200
        logging.info("login sucess!")
        try:
            eip_delete(tn, floating_ip_address)
        except Exception as e:
            tn.close()
            logging.exception(e)
            res = Response(json.dumps({'rc': 2002, 'msg': str(e)}), content_type='application/json')
            return res, 200
    return Response(json.dumps({'rc': 0, 'msg': 'sucess'}), content_type='application/json'), 200


def is_ip(address):
    try:
        IPy.IP(address)
        return "yes"
    except Exception as e:
        logging.error("floating_ip_address format is wrong:%s" % str(e))
        return str(e)


def is_ratelimit(ratelimit):
    try:
        num = int(ratelimit)
        if num > 0:
            return "yes"
        logging.info("paramter %s wrong" % ratelimit)
        return "paramter %s wrong" % ratelimit
    except Exception as e:
        logging.error("averateLimit format is wrong:%s" % str(e))
        return str(e)


def login(ip, username, password):
    ''' login module return tuple of vender and telnet fd '''
    ssh_newkey = 'Are you sure you want to continue connecting'
    ssh_cmd = 'ssh -o StrictHostKeyChecking=no %s@%s' % (username, ip)
    try:
        ssh = pexpect.spawn(ssh_cmd)
        try:
            index = ssh.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:'])
        except EOFError:
            try:
                ssh = pexpect.spawn(ssh_cmd, timeout=conf.TELNET_TIMEOUT)
                index = ssh.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:'])
            except EOFError:
                logging.error('SSH Connection refused')
                raise BaseException('SSH Connection refused')

    except Exception as e:
        print(e)
        if 'No buffer space available' in str(e):
            time.sleep(4)
            try:
                ssh = pexpect.spawn(ssh_cmd)
                try:
                    index = ssh.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:'])
                except EOFError:
                    try:
                        ssh = pexpect.spawn(ssh_cmd)
                        index = ssh.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:'])
                    except EOFError:
                        raise BaseException('SSH Connection refused')
            except Exception as e:
                raise BaseException('%s: %s' % (ip, e))
        else:
            raise BaseException('%s: %s' % (ip, e))
    if index == 0:
        raise BaseException('连接超时')
    elif index == 1:
        ssh.sendline('yes')
        index = ssh.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:'])
        if index != 2:
            raise BaseException('Login: SSH First Login Failed')
    if index == 2:
        try:
            ssh.sendline(password)
            index = ssh.expect(conf.prompt_match)
        except EOFError:
            raise BaseException('Login: SSH Login Failed')
        if index == 2:
            raise BaseException('连接超时')
    return ssh


def send_command(child, cmd):
    child.sendline(cmd)
    child.expect(PROMT)
    logging.info('cmd:%s,result:%s' % (cmd, child.before))


def eip_limit(tn, ip, tx, rx):
    for x in cmd:
        cmdline = ''
        for i in range(3):
            if x == b'sys' or x == b'inpolicy' or x == b'outpolicy':
                cmdline = cmd[x]
            elif x == b'outlimit':
                cmdline = cmd[x] % ({b'eip': bytes(ip, 'utf-8'), b'limit': bytes(tx, 'utf-8')})
            elif x == b'inlimit':
                cmdline = cmd[x] % ({b'eip': bytes(ip, 'utf-8'), b'limit': bytes(rx, 'utf-8')})
            elif x == b'inbehavior':
                cmdline = cmd[x] % ({b'limit': bytes(rx, 'utf-8')})
            elif x == b'outbehavior':
                cmdline = cmd[x] % ({b'limit': bytes(tx, 'utf-8')})
            elif x == b'incar':
                limit = str(1024 * int(rx))
                rate = str(64000 * int(rx))
                cmdline = cmd[x] % ({b'limit': bytes(limit, 'utf-8'), b'rate': bytes(rate, 'utf-8')})
            elif x == b'outcar':
                limit = str(1024 * int(tx))
                rate = str(64000 * int(tx))
                cmdline = cmd[x] % ({b'limit': bytes(limit, 'utf-8'), b'rate': bytes(rate, 'utf-8')})
            else:
                cmdline = cmd[x] % ({b'eip': bytes(ip, 'utf-8')})
            send_command(tn, cmdline)
            if is_cmd_effected(tn, cmdline):
                break
        else:
            tn.close()
            raise Exception('cmd:%s has no effect' % cmdline)
    tn.close()


def eip_delete(tn, ip):
    for i in range(3):
        for x in deletecmd:
            cmdline = ''
            if x == b'sys' or x == b'inpolicy' or x == b'outpolicy' or x == b'exit':
                cmdline = deletecmd[x]
            else:
                cmdline = deletecmd[x] % ({b'eip': bytes(ip, 'utf-8')})
            send_command(tn, cmdline)
        if not is_cmd_effected(tn, bytes(ip, 'utf-8'), b'dis curr', b'classifier '):
            break
    else:
        tn.close()
        raise Exception('deletecmd:%s has no effect' % cmdline)
    tn.close()


def is_cmd_effected(child, requirement, cmd=b'dis th', prefix=b''):
    child.sendline(b'%s | inc "%s"' % (cmd, requirement))
    child.expect(PROMT)
    logging.info('result:%s' % (child.before))
    pos = child.before.find(prefix + requirement)
    if pos < 0:
        time.sleep(0.5)
        return False
    return True


if __name__ == '__main__':
    init_route()
    app.run(host='0.0.0.0', port=30418, debug=False, threaded=True)  # socket
